﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace HobSoft.SoccerBet2014.Domain.Common.BfService
{
    using BfApi;
    using Domain.DataService;
    using Domain.Models;
    using Domain.Models.BfRaw;
    using Domain.Models.Site;
    using Site.Component;
    using SoccerBet2014.Common.Errors;

    public class BfManager : IBfManagerService
    {
        public const string APPKEY_HEADER = "X-Application";
        public const string SESSION_TOKEN_HEADER = "X-Authentication";
        protected const string SINGUP_URL = @"https://identitysso.betfair.com/api/certlogin";
        private readonly IBfEventDataService bfEventDataService;
        public BfManager(BFAccount account, IBfEventDataService _bfDataService)
        {
            bfEventDataService = _bfDataService;
            Account = account;
            Encoding = Encoding.UTF8;
            Timeout = 30000;
            loginInfo = new LoginInfo("sessionToken");
        }

        #region property
        public BFAccount Account
        {
            get;
            protected set;
        }

        protected LoginInfo loginInfo;
        public ILoginInfo LoginInfo
        {
            get { return loginInfo; }
        }
        public Encoding Encoding { get; set; }
        public int Timeout { get; set; }
        #endregion

        #region request
        protected virtual HttpWebRequest CreateRequest(Uri url, string type, string content, Action<HttpWebRequest> setRequest)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Timeout = Timeout;
            request.AutomaticDecompression = DecompressionMethods.GZip;
            request.KeepAlive = true;
            request.Method = type;
            request.ContentType = "application/json";
            request.Headers.Add(APPKEY_HEADER, Account.AppKey);
            request.Headers.Add("AcceptCharset", "utf-8");
            request.ServicePoint.Expect100Continue = false;
            if (setRequest != null)
                setRequest(request);

            if (url.Scheme.Equals("https"))
            {
                ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(RemoteCertificateCheckValidationResult);
                request.ProtocolVersion = HttpVersion.Version10;
            }

            if (!string.IsNullOrEmpty(content) && type.Equals("POST", StringComparison.OrdinalIgnoreCase))
            {
                Stream reqStream = request.GetRequestStream();
                byte[] reqBuffer = Encoding.GetBytes(content);
                reqStream.Write(reqBuffer, 0, reqBuffer.Length);
                reqStream.Close();
            }
            return request;
        }

        private static bool RemoteCertificateCheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors errors)
        {
            return true; //总是接受  
        }

        #endregion

        #region signup
        public async Task<bool> Signup(string certificateFile)
        {
            if (string.IsNullOrEmpty(certificateFile))
                Error.ThrowNull("certificateFile", "需要证书文件");
            if (!System.IO.File.Exists(certificateFile))
                Error.ThrowNull("certificateFile", "需要证书文件，指定路径找不到");

            string url = SINGUP_URL;
            string content = string.Format("username={0}&password={1}", Account.Account, Account.Password);
            X509Certificate cert = X509Certificate.CreateFromCertFile(certificateFile);
            var request = CreateRequest(new Uri(url), "POST", content, rq =>
            {
                rq.ContentType = "application/x-www-form-urlencoded";
                rq.ClientCertificates.Add(cert);
            });
            WebResponse response = null;
            string resContent = string.Empty;
            try
            {
                var resTask = request.GetResponseAsync();
                response = await resTask;
                if (resTask.IsCanceled || resTask.Exception != null)
                    return false;

                var resStream = response.GetResponseStream();
                StreamReader reader = new StreamReader(resStream, Encoding);
                resContent = reader.ReadToEnd();
            }
            catch
            {
                throw;
            }
            finally
            {
                if (response != null)
                    response.Dispose();
                request.Abort();
            }
            return ResolveSignupResult(resContent);
        }

        protected bool ResolveSignupResult(string resContent)
        {
            var json = (JObject)JsonConvert.DeserializeObject(resContent);
            var token = json.Value<string>("sessionToken");
            var status = json.Value<string>("loginStatus");
            if (status == "SUCCESS")
            {
                loginInfo.ResetLogin(token);
                Account.Session = token;
                Account.LastRequestTime = DateTime.Now;
                if (bfEventDataService != null)
                    bfEventDataService.UpdateSessionToke(Account);
                return true;
            }

            return false;
        }
        #endregion

        #region betting method
        public async Task<IList<MarketBook>> ListMarketBook(params string[] marketIds)
        {
            if (!LoginInfo.IsLogin)
                Error.ThrowInvalidOperation("必须登录必发服务器");
            ApiBettingRestClient client = new ApiBettingRestClient(Account.AppKey, Account.Session);
            var result = await client.ListMarketBook(marketIds);
            loginInfo.Reset(client.LastResponseResult);
            return result;
        }

        public async Task<PlaceExecutionReport> Betting(string _marketId, PlaceInstruction _placeInstruction, string _customerRef = null)
        {
            if (!LoginInfo.IsLogin)
                Error.ThrowInvalidOperation("必须登录必发服务器");
            ApiBettingRestClient client = new ApiBettingRestClient(Account.AppKey, Account.Session);
            var result = await client.Betting(_marketId, _placeInstruction, _customerRef);
            loginInfo.Reset(client.LastResponseResult);
            return result;
        }

        public async Task<CurrentOrderSummaryReport> ListOrders()
        {
            if (!LoginInfo.IsLogin)
                Error.ThrowInvalidOperation("必须登录必发服务器");
            ApiBettingRestClient client = new ApiBettingRestClient(Account.AppKey, Account.Session);
            var result = await client.ListOrders();
            loginInfo.Reset(client.LastResponseResult);
            return result;
        }

        public async Task<CancelExecutionReport> CancelOrders(string _marketId, CancelInstruction _instruction, string _customerRef = null)
        {
            if (!LoginInfo.IsLogin)
                Error.ThrowInvalidOperation("必须登录必发服务器");
            ApiBettingRestClient client = new ApiBettingRestClient(Account.AppKey, Account.Session);
            var result = await client.CancelOrders(_marketId, _instruction, _customerRef);
            loginInfo.Reset(client.LastResponseResult);
            return result;
        }

        #endregion
    }
}
